﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Evan.Security
{
    /// <summary>
    /// 文件安全检查
    /// 根据文件前几个字节来判断文件是否合法
    /// </summary>
    public static class FileCheck
    {
        
        private static Dictionary<string, string> mFileTypes = new Dictionary<string, string>();

        private static Dictionary<string, string> FileTypes_All()
        {
            mFileTypes.Clear();
            mFileTypes.Add("FFD8FFE000", ",jpg,jpeg,bmp,");
            mFileTypes.Add("424D", ",bmp,");
            mFileTypes.Add("89504E470D", ",png,");
            mFileTypes.Add("4749463839", ",gif,");
            mFileTypes.Add("255044462D", ",pdf,ai,");
            mFileTypes.Add("3842505300", ",psd,");
            mFileTypes.Add("52494646", ",cdr,");
            mFileTypes.Add("000000", ",mp4,");
            mFileTypes.Add("49443303", ",mp3,");
            //mFileTypes.Add("FFFBE0C4", ",mp3,"); //这个是不正常的mp3
            mFileTypes.Add("1F8B0800", ",ipk,");
            mFileTypes.Add("D0CF11E0A1", ",doc,xls,ppt,wps,vsd,");
            mFileTypes.Add("504B0304", ",docx,xlsx,pptx,vsdx,zip,apk,");
            mFileTypes.Add("526172211A", ",rar,");
            mFileTypes.Add("377ABCAF27", ",7z,");
            return mFileTypes;
        }
        
        private static Dictionary<string, string> FileTypes()
        {
            mFileTypes.Clear();
            mFileTypes.Add("FFD8", ",jpg,jpeg,bmp,");
            mFileTypes.Add("424D", ",bmp,");
            mFileTypes.Add("8950", ",png,");
            mFileTypes.Add("4749", ",gif,");
            mFileTypes.Add("2550", ",pdf,ai,");
            mFileTypes.Add("3842", ",psd,");
            mFileTypes.Add("5249", ",cdr,");
            mFileTypes.Add("0000", ",mp4,");
            mFileTypes.Add("4944", ",mp3,");
            //mFileTypes.Add("FFFB", ",mp3,"); //这个是不正常的mp3
            mFileTypes.Add("1F8B", ",ipk,");
            mFileTypes.Add("D0CF", ",doc,xls,ppt,wps,vsd,");
            mFileTypes.Add("504B", ",docx,xlsx,pptx,vsdx,zip,apk,");
            mFileTypes.Add("5261", ",rar,");
            mFileTypes.Add("377A", ",7z,");
            return mFileTypes;
        }

        /// <summary>
        /// 文件检查
        /// </summary>
        /// <param name="ByteStr">文件头信息  2字节</param>
        /// <param name="FileExt">文件后缀 , 默认为空</param>
        /// <returns></returns>
        public static string FileChecker(string ByteStr, string FileExt = "")
        {
            if (FileTypes().ContainsKey(ByteStr))
            {
                //头信息包含在里面的 , 都是可以允许上传的
                return "";
            }
            else
            {
                return "文件头信息不支持，请勿上传非法文件";
            }
        }

        /// <summary>
        /// 根据文件路径来判断文件是否合规
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool FileIsAllow(string filePath)
        {
            return false;
        }
        //public static bool FileIsAllow(string filePath)
        //{
        //    return false;
        //}

        /// <summary>
        /// 获取文件头信息
        /// <para>默认从文件头开始获取两个字节</para>
        /// </summary>
        /// <param name="fullpath">文件路径 , 全路径</param>
        /// <param name="offset">偏移量 默认0</param>
        /// <param name="bufferSize">读取字节 默认2</param>
        /// <returns></returns>
        public static string FileSafeCheck(string fullpath, int offset = 0, long bufferSize = 2)
        {
            byte[] b = GetFilePackageBytes(fullpath, offset, bufferSize);
            string str = string.Concat(b.Select(res => string.Format("{0:X2} ", res))).Replace(" ", "");
            return str;
        }

        /// <summary>
        /// 转换器 , byte转为string
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        private static string BitConvertToStr(byte[] b)
        {
            string res = "";
            foreach (var item in b)
            {
                res += item.ToString();
            }
            return res;
        }
    
        /// <summary>
        /// 获取指定文件的 字节片段
        /// 调用： byte[] bytes = GetFilePackageBytes("D:\Test.rmvb",0,10);
        /// 测试过：4个 G的 电影，想读取哪一段，就能读取 哪一段。而且，内存 始终保持在 30M 以下。
        /// </summary>
        /// <param name="fullPath">全路径 , 物理路径 , 包含盘符</param>
        /// <param name="offset">偏移量 0 为从0字节开始读取</param>
        /// <param name="bufferSize">读取几个字节</param>
        /// <returns></returns>
        private static byte[] GetFilePackageBytes(string fullPath, long offset = 0, long bufferSize = 10)
        {
            if (!File.Exists(fullPath)) return null;

            if (offset < 0) offset = 0;
            if (bufferSize < 0) bufferSize = long.MaxValue;

            const int PACKAGE_SIZE = 1024 * 8;

            using (FileStream myFs = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                myFs.Position = offset;
                long filePackageLength = Math.Min(bufferSize, myFs.Length - myFs.Position);
                byte[] bytes = new byte[filePackageLength];

                long readLength = 0;
                while (readLength < filePackageLength)
                {
                    long leaveLength = myFs.Length - myFs.Position;
                    long leaveLength2 = filePackageLength - readLength;
                    int byteLength = (leaveLength > (long)PACKAGE_SIZE) ? PACKAGE_SIZE : Convert.ToInt32(leaveLength);
                    byteLength = (leaveLength2 > (long)byteLength) ? byteLength : Convert.ToInt32(leaveLength2);

                    byte[] buffer = new byte[byteLength];
                    myFs.Read(buffer, 0, byteLength);

                    Array.Copy(buffer, 0, bytes, readLength, byteLength);
                    readLength = readLength + byteLength;

                    if (myFs.Position >= myFs.Length) break;
                }

                myFs.Close();
                return bytes;
            }
        }

    }
}
